package utils;

import java.util.Iterator;
import java.util.Vector;

import problem.Heuristic;
import agents.Agent;
import agents.DummyAgent;
import agents.Search;
import env.City;
import env.Move;
import env.World;

/***
 * this class have similar functionalty to Dijkstra but for the ai searches it
 * will build a search tree and will have a node class it will recive the world
 * and an expansion strategy, some weighted queue for example
 * 
 * @author lior-13
 * 
 */
public class AiSearch {
	

	public class AiNode implements Comparable<AiNode> {
		public City c;
		public AiNode parent;
		public int cost;
		public AiSearch search;
		public World copiedWorld;
		public Move move; // move leading to this node
		public String toString()
		{
			return move+" node cost: "+cost;
		}
		public boolean equals(Object other)
		{
			
			return c.equals(((AiNode)other).c) && move.equals(((AiNode)other).move);
		}
		public AiNode(AiSearch search, City c, AiNode parent, int cost, World world) {
			this.c = world.getCityByName(c.getName());
			this.cost = cost;
			this.parent = parent;
			this.copiedWorld = world;
			this.search=search;
		}

		public void expand() {
			expands++;// counting expands
			
			//System.out.println("city:" +c.toString().substring(1,2)+"###"+expands);
			
			if (c.getNeighbors().isEmpty())// not supposed to happen but in just in
									// case
				return;
			Iterator<City> it = c.getNeighbors().iterator();
			while (it.hasNext()) {
				City next = (City) it.next();
				if (this.copiedWorld.getRoadBetweenCities(c, next).isBlocked())
					continue;// to avoid blocked roads
				World copyOfcopy = new World(this.copiedWorld);
				AiNode aiNode = new AiNode(search,next, this, 0,copyOfcopy );// cost
																		// will
																		// be
																		// calculated
																		// by
																		// strategy
				aiNode.move = new Move(search.agent, origWorld.getRoadBetweenCities(c,
						next), origWorld.getCityByName(c.getName()),
						origWorld.getCityByName(next.getName()), false, false);
				if (copiedWorld.getCityByName(c.getName()).getChems() > 0) {// moving chems TODO make sure this is ok
					aiNode.copiedWorld.getCityByName(c.getName()).addChems(-1);
					aiNode.copiedWorld.getCityByName(next.getName()).addChems(1);
					/*copyOfcopy.getCityByName(c.getName()).addChems(-1);
					copyOfcopy.getCityByName(next.getName()).addChems(1);*/
					aiNode.move.chems = true;
				}
				strategy.calcCost(this, aiNode, move);
				
				queue.add(aiNode);
			}
			this.copiedWorld = null;
		}

		@Override
		public int compareTo(AiNode o) {
			return this.cost - o.cost;
		}

		public Move getMove() {
			AiNode it = this;
			if (parent == null)
				return null;

			while (it.parent.parent != null) {
			
				it = it.parent;
			}
			Move res = it.move;
			it.parent = null;
			return res;
		}

	}

	private City init;
	private City goal;
	private PriorityQueueNoDupes<AiNode> queue;
	private Strategy strategy;
	private int cutOff;
	private int expands;
	private World origWorld;
	private World copiedWorld;
	public Agent agent;
	public AiSearch(Search agent,World origWorld, World copiedWorld, City init, City goal,
			Strategy strategy, int cutOff) {
		this.init = init;
		this.goal = goal;
		queue = new PriorityQueueNoDupes<AiNode>();
		this.strategy = strategy;
		this.cutOff = cutOff;
		this.expands = 0;
		this.origWorld = origWorld;
		this.copiedWorld=copiedWorld;
		this.agent=agent;

	}

	public AiNode search() {
		queue.add(new AiNode(this,init, null, Heuristic.getHeuristic(this.copiedWorld,
				goal), this.copiedWorld));
		while (!queue.isEmpty()) {
			//AiNode a = queue.iterator();
			AiNode a = queue.remove();
			//System.out.println(a);
			if (goalNode(a))
				return a;
			if (cutOff > 0 && expands == cutOff) {
				return a;
			}
			a.expand();
		}
		return null;
	}

	/**
	 * returns true if all chems are cleared
	 * 
	 * @param a
	 * @return
	 */
	private boolean goalNode(AiNode a) {
		
		/*Iterator<City> it = a.copiedWorld.getCities().iterator();
		while (it.hasNext()) {
			City city = (City) it.next();
			if (city.getChems() > 0)
				return false;
		}
		
		return true;*/
		boolean ans = a.c.equals(goal) && a.move.chems==true && !a.copiedWorld.chemsOutsideCity(goal);
		
		return ans;
	}

}
